﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved
// Developer:  Michael Antonio
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
#if SILVERLIGHT
#if !WINDOWS_PHONE
using System.Json;
#endif
using System.Text.RegularExpressions;
#endif

namespace MichMan.Utilities
{
    public static class ListHelper
    {
        public static List<T> ListFromObject<T>(T o)
        {
            List<T> list = new List<T>();
            list.Add(o);
            return list;
        }

        public static IEnumerable<T> Pivot<T>(this IEnumerable<T> list, Func<T, bool> predicate)
        {
            if (list.Count() == 1 || list.Count() == 0)
            {
                return Enumerable.AsEnumerable(list);
            }

            IEnumerable<T> newStart = list.SkipWhile(i => !predicate(i));
            IEnumerable<T> newEnd = list.TakeWhile(i => !predicate(i));

            return Enumerable.Union(newStart, newEnd);
        }
    }

    public static class StringCollectionHelper
    {
        public delegate bool TryGetDelegate(string key, out string value);
        public delegate string GetDelegate(string key);
        public delegate void SetDelegate(string key, string value);

        public static bool TryGet<T>(TryGetDelegate del, string key, out T value) where T : struct
        {
            string str;
            if (del(key, out str))
            {
                return ParseUtility.TryParse<T>(str, out value);
            }
            value = default(T);
            return false;
        }

        public static T? GetNullable<T>(GetDelegate del, string key) where T : struct
        {
            T value;
            if (ParseUtility.TryParse<T>(del(key), out value))
            {
                return value;
            }
            return null;
        }

        public static T Get<T>(GetDelegate del, string key)
        {
            return Get<T>(del, key, default(T));
        }

        public static T Get<T>(GetDelegate del, string key, T defaultValue)
        {
            return ParseUtility.SafeParse<T>(del(key), defaultValue);
        }

        public static void Set<T>(SetDelegate del, string key, T value)
        {
            del(key, value.ToString());
        }
    }

    public interface IBag<TKey, TValue>
    {
        TValue this[TKey key] { get; set; }
    }

    public class Bag<TKey, TValue> : IBag<TKey, TValue>
    {
        Dictionary<TKey, TValue> _backing;
        public Bag()
        {
            _backing = new Dictionary<TKey, TValue>();
        }

        public Bag(Dictionary<TKey, TValue> backing)
        {
            _backing = backing;
        }

        public TValue this[TKey key]
        {
            get { return _backing[key]; }
            set { _backing[key] = value; }
        }
    }

    public class ThreadSafeBag<TKey, TValue> : IBag<TKey, TValue>
    {
        private static readonly object _syncRoot = new object();
        Dictionary<TKey, TValue> _backing = new Dictionary<TKey, TValue>();

        public ThreadSafeBag()
        {
        }

        /// <summary>
        /// Initialize the backing data as an atomic operation.
        /// </summary>
        /// <param name="initBacking"></param>
        public ThreadSafeBag(Action<IBag<TKey, TValue>> initBacking)
        {
            // No need for locking during initialization
            initBacking(new Bag<TKey, TValue>(_backing));
        }

        public TValue AddIfNotContains(TKey key, Func<TValue> valueFunc)
        {
            if (_backing.ContainsKey(key))
            {
                return _backing[key];
            }
            lock (_syncRoot)
            {
                // May have been filled in while we were waiting for the lock.
                if (_backing.ContainsKey(key))
                {
                    return _backing[key];
                }

                _backing[key] = valueFunc();
            }

            return _backing[key];
        }

        public TValue this[TKey key]
        {
            get
            {
                return _backing[key];
            }
            set
            {
                lock (_syncRoot)
                {
                    _backing[key] = value;
                }
            }
        }
    }

    // Extension methods for well known classes.
    public static class StringDictionaryExtensions
    {
        public static bool TryGet<T>(this IDictionary<string, string> dict, string key, out T value) where T : struct
        {
            return StringCollectionHelper.TryGet<T>((string k, out string v) => { if (dict.ContainsKey(k)) { v = dict[k]; return true; } v = null; return false; }, key, out value);
        }

        public static T Get<T>(this IDictionary<string, string> dict, string key)
        {
            return StringCollectionHelper.Get<T>(k => dict[k], key);
        }

        public static T Get<T>(this IDictionary<string, string> dict, string key, T defaultValue)
        {
            return StringCollectionHelper.Get<T>(k => dict[k], key, defaultValue);
        }

        public static T? GetNullable<T>(this IDictionary<string, string> dict, string key) where T : struct
        {
            return StringCollectionHelper.GetNullable<T>(k => dict[k], key);
        }

        public static void Set<T>(this IDictionary<string, string> dict, string key, T value)
        {
            StringCollectionHelper.Set<T>((k, v) => dict[k] = v, key, value);
        }
#if FALSE
        public static T Get<T>(this NameValueCollection dict, string key)
        {
            return StringCollectionHelper.Get<T>(k => dict[k], key);
        }

        public static T Get<T>(this NameValueCollection dict, string key, T defaultValue)
        {
            return StringCollectionHelper.Get<T>(k => dict[k], key, defaultValue);
        }

        public static T? GetNullable<T>(this NameValueCollection dict, string key) where T : struct
        {
            return StringCollectionHelper.GetNullable<T>(k => dict[k], key);
        }

        public static void Set<T>(this NameValueCollection dict, string key, T value)
        {
            StringCollectionHelper.Set<T>((k, v) => dict[k] = v, key, value);
        }
#else
#if FALSE
        public static T Get<T>(this JsonObject dict, string key)
        {
            return Get<T>(dict, key, default(T));
        }

        private static string JsonGet(JsonObject dict, string key)
        {
            if (dict.ContainsKey(key))
            {
                JsonValue value = dict[key];
                if (value != null)
                {
                    switch (value.JsonType)
                    {
                        case JsonType.Array:
                        case JsonType.Object:
                        case JsonType.Number:
                            return value.ToString();
                        case JsonType.String:
                            {
                                if (value.ToString().StartsWith("\"\\/Date(") && value.ToString().EndsWith(")\\/\""))
                                {
                                    return ((DateTime)value).ToString();
                                }
                                return (string)value;
                            }
                    }
                }
            }
            return null;
        }

        private static void JsonSet(JsonObject dict, string key, string value)
        {
            dict[key] = value;
        }

        public static T Get<T>(this JsonObject dict, string key, T defaultValue)
        {
            return StringCollectionHelper.Get<T>(k => JsonGet(dict, k), key, defaultValue);
        }

        public static T? GetNullable<T>(this JsonObject dict, string key) where T : struct
        {
            return StringCollectionHelper.GetNullable<T>(k => JsonGet(dict, k), key);
        }

        public static void Set<T>(this JsonObject dict, string key, T value)
        {
            StringCollectionHelper.Set<T>((k, v) => JsonSet(dict, k, v), key, value);
        }
#endif
#endif
    }
}
